#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h> 
#include <linux/irq.h>

#define F1C200IRQ_CNT 	1 				/* 设备号个数 */ 
#define F1C200IRQ_NAME 	"f1c200irq" 	/* 名字 */ 
#define KEY0VALUE 		0X01 			/* KEY0按键值 */ 
#define INVAKEY 		0XFF 			/* 无效的按键值 */ 
#define KEY_NUM 		2 				/* 按键数量 */

struct keydesc_st
{
	int gpio;
	int irqnum;
	unsigned char value;
	unsigned char name[10];
	irqreturn_t (*handler)(int, void *);
};

struct f1c200irq_st
{
	unsigned char curkeynum;
	unsigned char curkeystat;
	dev_t devid;
	int major;
	int minor;
	int timerperiod; 		//单位ms
	struct cdev cdev;
	struct class *class;
	struct device *device;
	struct device_node *node;
	struct timer_list timer;
	struct keydesc_st key[KEY_NUM];
};

static struct f1c200irq_st f1c200irq;

static int f1c200irq_open(struct inode *inode, struct file *flip)
{
	return 0;
}

static ssize_t f1c200irq_read(struct file *flip, char __user *buf, size_t cnt, loff_t *off_t)
{
	int ret =0;
	unsigned char keyvalue[2]={0};

	if(f1c200irq.curkeystat ==1 || f1c200irq.curkeystat ==2)
	{
		keyvalue[0] = f1c200irq.curkeynum;
		keyvalue[1] = f1c200irq.curkeystat;
		ret = copy_to_user(buf, keyvalue, sizeof(keyvalue));
		if(ret <0)
		{
			return -EINVAL;
		}
	}
	else
		return -EINVAL;


	return 0;
}

static struct file_operations f1c200irq_ops = 
{
	.owner 	= THIS_MODULE,
	.open 	= f1c200irq_open,
	.read 	= f1c200irq_read,
};

static irqreturn_t key0_handler(int irq, void *ptr)
{
	f1c200irq.curkeynum =0;
	mod_timer(&f1c200irq.timer, jiffies+msecs_to_jiffies(10));
	
	return IRQ_RETVAL(IRQ_HANDLED);
}

static irqreturn_t key1_handler(int irq, void *ptr)
{
	f1c200irq.curkeynum =1;
	mod_timer(&f1c200irq.timer, jiffies+msecs_to_jiffies(10));
	
	return IRQ_RETVAL(IRQ_HANDLED);
}


static void timer_function(struct timer_list * timer)
{
	unsigned char value,currentValue;

	value = f1c200irq.key[f1c200irq.curkeynum].value;
	currentValue = gpio_get_value(f1c200irq.key[f1c200irq.curkeynum].gpio);

	if(value ==1 && currentValue ==1)
		f1c200irq.curkeystat = 0;
	else if(value ==1 && currentValue ==0)
		f1c200irq.curkeystat = 1;
	else if(value ==0 && currentValue ==1)
		f1c200irq.curkeystat = 2;
	else
		f1c200irq.curkeystat = 0;

	printk("curkeynum:%d curkeystat:%d\r\n",f1c200irq.curkeynum, f1c200irq.curkeystat);
	f1c200irq.key[f1c200irq.curkeynum].value = currentValue;
}

static int f1c200key_init(void)
{
	int i;
	int ret;

	f1c200irq.node = of_find_node_by_path("/my-keys");
	if(f1c200irq.node ==NULL)
		return -EINVAL;
	
	for(i=0;i<KEY_NUM;i++)
	{
		f1c200irq.key[i].gpio = of_get_named_gpio(f1c200irq.node, "gpios", i);
		if(f1c200irq.key[i].gpio <0)
			return -EINVAL;

		memset(f1c200irq.key[i].name, 0,sizeof(f1c200irq.key[i].name));
		sprintf(f1c200irq.key[i].name,"key%d",i);
		
		ret = gpio_request(f1c200irq.key[i].gpio, f1c200irq.key[i].name);
		if(ret <0)
			return -EINVAL;

		ret = gpio_direction_input(f1c200irq.key[i].gpio);
		if(ret <0)
			return -EINVAL;	
		
		f1c200irq.key[i].irqnum = irq_of_parse_and_map(f1c200irq.node, i);
		
		f1c200irq.key[i].value = KEY0VALUE;
		f1c200irq.key[0].handler = key0_handler;
		f1c200irq.key[1].handler = key1_handler;
		
		ret = request_irq(f1c200irq.key[i].irqnum, f1c200irq.key[i].handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, f1c200irq.key[i].name, NULL);
		if(ret <0)
		{
			return -EFAULT;
		}
	}

	timer_setup(&f1c200irq.timer, timer_function, 0);

	return 0;
}

static int __init f1c200irq_init(void)
{
	int ret = 0;

	//申请设备号
	if(f1c200irq.major)
	{
		f1c200irq.devid = MKDEV(f1c200irq.major, 0);
		ret = register_chrdev_region(f1c200irq.devid, F1C200IRQ_CNT, F1C200IRQ_NAME);
		if(ret <0)
		{
			return -EINVAL;
		}
	}
	else
	{
		ret = alloc_chrdev_region(&f1c200irq.devid, 0, F1C200IRQ_CNT, F1C200IRQ_NAME);
		if(ret<0)
		{
			return -EINVAL;
		}
		f1c200irq.major = MAJOR(f1c200irq.devid);
		f1c200irq.minor = MINOR(f1c200irq.devid);
	}
	//创建字符设备
	f1c200irq.cdev.owner = THIS_MODULE;
	cdev_init(&f1c200irq.cdev, &f1c200irq_ops);
	ret = cdev_add(&f1c200irq.cdev, f1c200irq.devid, F1C200IRQ_CNT);
	if(ret <0)
	{
		return -EINVAL;
	}

	//创建类
	f1c200irq.class = class_create(THIS_MODULE, F1C200IRQ_NAME);
	if(IS_ERR(f1c200irq.class))
	{
		return PTR_ERR(f1c200irq.class);
	}

	//创建设备
	f1c200irq.device = device_create(f1c200irq.class, NULL, f1c200irq.devid, NULL, F1C200IRQ_NAME);
	if(IS_ERR(f1c200irq.device))
	{
		return PTR_ERR(f1c200irq.device);
	}

	//初始化按键
	ret = f1c200key_init();
	if(ret <0)
	{
		return -EINVAL;
	}
	
	return 0;
}

static void __exit f1c200irq_exit(void)
{
	int i=0;

	//释放中断
	for(i=0;i<KEY_NUM;i++)
	{
		free_irq(f1c200irq.key[i].irqnum, NULL);
		gpio_free(f1c200irq.key[i].gpio);
	}

	//卸载字符设备
	cdev_del(&f1c200irq.cdev);
	unregister_chrdev_region(f1c200irq.devid, F1C200IRQ_CNT);

	//卸载设备
	device_destroy(f1c200irq.class, f1c200irq.devid);
	class_destroy(f1c200irq.class);
}

module_init(f1c200irq_init);
module_exit(f1c200irq_exit);
MODULE_AUTHOR("jtb");
MODULE_LICENSE("GPL");

